<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>xeokit Example</title>
    <link href="../css/pageStyle.css" rel="stylesheet"/>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/js/all.min.js"></script>
    <script src="../libs/dat.gui.min.js" type="text/javascript"></script>
    <link href="../css/dat-gui-light-style.css" rel="stylesheet"/>
</head>
<body>
<input type="checkbox" id="info-button"/>
<label for="info-button" class="info-button"><i class="far fa-3x fa-question-circle"></i></label>
<canvas id="myCanvas"></canvas>
<div class="slideout-sidebar">
    <img class="info-icon" src="../../assets/images/performance_icon.png"/>
    <h1>FastNavPlugin</h1>
    <h2>Makes interaction smoother</h2>
    <p>Whenever the camera moves, <a href="../../docs/class/src/plugins/FastNavPlugin/FastNavPlugin.js~FastNavPlugin.html"
                                     target="_other">FastNavPlugin</a> temporarily reduces the canvas resolution and
        disables certain rendering
        effects, to make interaction smoother. It also uses callback to hide elements of specific hide during navigation.</p>
    <h3>Stats</h3>
    <ul>
        <li>
            <div id="time">Loading JavaScript modules...</div>
        </li>
    </ul>
    <h3>Customize</h3>
    <div id="myDatGuiContainer"></div>
    <h3>Components Used</h3>
    <ul>
        <li>
            <a href="../../docs/class/src/viewer/Viewer.js~Viewer.html"
               target="_other">Viewer</a>
        </li>
        <li>
            <a href="../../docs/class/src/plugins/XKTLoaderPlugin/XKTLoaderPlugin.js~XKTLoaderPlugin.html"
               target="_other">XKTLoaderPlugin</a>
        </li>
        <li>
            <a href="../../docs/class/src/plugins/FastNavPlugin/FastNavPlugin.js~FastNavPlugin.html"
               target="_other">FastNavPlugin</a>
        </li>
        <li>
            <a href="http://openifcmodel.cs.auckland.ac.nz/Model/Details/316"
               target="_other">Model source</a>
        </li>
    </ul>
</div>
</body>
<script type="module">

    import {
        FastNavPlugin,
        Viewer,
        XKTLoaderPlugin,
    } from "../../dist/xeokit-sdk.min.es.js";

    //------------------------------------------------------------------------------------------------------------------
    // Create a Viewer
    //------------------------------------------------------------------------------------------------------------------

    const viewer = new Viewer({
        canvasId: "myCanvas",
        transparent: true,
        pbrEnabled: false
    });

    viewer.scene.sao.enabled = true; // Higher-quality SAO settings
    viewer.scene.sao.numSamples = 60;
    viewer.scene.sao.kernelRadius = 170;

    viewer.scene.camera.eye = [-66.26, 105.84, -281.92];
    viewer.scene.camera.look = [42.45, 49.62, -43.59];
    viewer.scene.camera.up = [0.05, 0.95, 0.15];

    viewer.scene.edgeMaterial.edgeAlpha = 0.3;
    viewer.scene.edgeMaterial.edgeColor = [0, 0, 0];

    //------------------------------------------------------------------------------------------------------------------
    // Create a callback function that hides elements of specified type and returns callback which reverts that behaviour
    //------------------------------------------------------------------------------------------------------------------

    function hideElementsWithTypes() {
        let typesToHide = ["IfcDoor", "IfcFurnishingElement", "IfcRailing"]; // In this particular example we decide to hide these types during navigation
        let elementsWithChangedVisibility = [];

        if (typesToHide && typesToHide.length > 0) {
            let objects = viewer.scene.objects;
            for (const [key, value] of Object.entries(objects)) {
                let metaObjects = viewer.metaScene.metaObjects;
                let currentMetaObject = metaObjects[key];
                if (typesToHide.includes(currentMetaObject.type)){
                    if (value.visible) {
                        value.visible = false;
                        elementsWithChangedVisibility.push(value);
                    }
                }
            }
        }

        return function revert() {
            elementsWithChangedVisibility.forEach((element) => element.visible = true);
            elementsWithChangedVisibility = [];
        }
    }

    //------------------------------------------------------------------------------------------------------------------
    // Install FastNavPlugin
    //------------------------------------------------------------------------------------------------------------------

    const fastNavPlugin = new FastNavPlugin(viewer, {
        hideEdges: true,
        hideSAO: true,
        hideColorTexture: true,
        hidePBR: true,
        hideTransparentObjects: false,
        scaleCanvasResolution: true,
        defaultScaleCanvasResolutionFactor: 1.0,
        scaleCanvasResolutionFactor: 0.5,
        delayBeforeRestore: true,
        delayBeforeRestoreSeconds: 0.4,
        onMoved: hideElementsWithTypes,
    });

    //------------------------------------------------------------------------------------------------------------------
    // Load a model
    //------------------------------------------------------------------------------------------------------------------

    const xktLoader = new XKTLoaderPlugin(viewer);

    const t0 = performance.now();

    document.getElementById("time").innerHTML = "Loading model...";

    const sceneModel = xktLoader.load({
        id: "myModel",
        src: "../../assets/models/xkt/v8/ifc/OTCConferenceCenter.ifc.xkt",
        saoEnabled: true,
        edges: false,
        dtxEnabled: true,
        objectDefaults: { // This model has opaque windows / spaces; make them transparent
            "IfcPlate": {
                opacity: 0.3 // These are used as windows in this model - make transparent
            },
            "IfcWindow": {
                opacity: 0.4
            },
            "IfcSpace": {
                opacity: 0.4
            }
        }
    });

    sceneModel.on("loaded", () => {
        const t1 = performance.now();
        document.getElementById("time").innerHTML = `Model loaded in ${Math.floor(t1 - t0) / 1000.0} seconds<br>Objects: ${sceneModel.numEntities}`;
    });

    window.viewer = viewer;

    //------------------------------------------------------------------------------------------------------------------
    // GUI to play with FastNavPlugin, SAO and camera configurations
    //------------------------------------------------------------------------------------------------------------------

    const edgeParams = new function () {
        this.edges = viewer.scene.edgeMaterial.edges;
    }();

    const pbrParams = new function () {
        this.pbrEnabled = viewer.scene.pbrEnabled;
    }();

    const saoParams = new function () {
        const sao = viewer.scene.sao;
        const camera = viewer.scene.camera;
        this.enabled = sao.enabled;
        this.kernelRadius = sao.kernelRadius;
        this.intensity = sao.intensity;
        this.bias = sao.bias;
        this.scale = sao.scale;
        this.minResolution = sao.minResolution;
        this.numSamples = sao.numSamples;
        this.blendFactor = sao.blendFactor;
        this.blendCutoff = sao.blendCutoff;
        this.blur = sao.blur;
        this.perspective = (camera.projection === "perspective");
        this.far = camera.perspective.far;
        this.fov = camera.perspective.fov;
    }();

    const fastNavParams = new function () {
        this.hidePBR = fastNavPlugin.hidePBR;
        this.hideSAO = fastNavPlugin.hideSAO;
        this.hideEdges = fastNavPlugin.hideEdges;
        this.hideTransparentObjects = fastNavPlugin.hideTransparentObjects;
        this.scaleCanvasResolution = fastNavPlugin.scaleCanvasResolution;
        this.scaleCanvasResolutionFactor = fastNavPlugin.scaleCanvasResolutionFactor;
        this.delayBeforeRestore = fastNavPlugin.delayBeforeRestore;
        this.delayBeforeRestoreSeconds = fastNavPlugin.delayBeforeRestoreSeconds;
    }();

    const update = function () {

        const sao = viewer.scene.sao;
        const camera = viewer.scene.camera;

        viewer.scene.edgeMaterial.edges = edgeParams.edges;

        viewer.scene.pbrEnabled = pbrParams.pbrEnabled;

        sao.enabled = saoParams.enabled;
        sao.kernelRadius = saoParams.kernelRadius;
        sao.intensity = saoParams.intensity;
        sao.bias = saoParams.bias;
        sao.scale = saoParams.scale;
        sao.minResolution = saoParams.minResolution;
        sao.numSamples = saoParams.numSamples;
        sao.blendFactor = saoParams.blendFactor;
        sao.blendCutoff = saoParams.blendCutoff;
        sao.blur = saoParams.blur;

        camera.projection = saoParams.perspective ? "perspective" : "ortho";
        camera.perspective.far = saoParams.far;
        camera.ortho.far = saoParams.far;
        camera.perspective.fov = saoParams.fov;

        fastNavPlugin.hidePBR = fastNavParams.hidePBR;
        fastNavPlugin.hideSAO = fastNavParams.hideSAO;
        fastNavPlugin.hideEdges = fastNavParams.hideEdges;
        fastNavPlugin.hideTransparentObjects = fastNavParams.hideTransparentObjects;
        fastNavPlugin.scaleCanvasResolution = fastNavParams.scaleCanvasResolution;
        fastNavPlugin.scaleCanvasResolutionFactor = fastNavParams.scaleCanvasResolutionFactor;
        fastNavPlugin.delayBeforeRestore = fastNavParams.delayBeforeRestore;
        fastNavPlugin.delayBeforeRestoreSeconds = fastNavParams.delayBeforeRestoreSeconds;
        requestAnimationFrame(update);
    };

    update();

    const gui = new dat.GUI({autoPlace: false, width: "100%"});

    const fastNavFolder = gui.addFolder('FastNavPlugin');
    fastNavFolder.add(fastNavParams, 'hidePBR');
    fastNavFolder.add(fastNavParams, 'hideSAO');
    fastNavFolder.add(fastNavParams, 'hideEdges');
    fastNavFolder.add(fastNavParams, 'hideTransparentObjects');
    fastNavFolder.add(fastNavParams, 'scaleCanvasResolution');
    fastNavFolder.add(fastNavParams, 'scaleCanvasResolutionFactor', 0.0, 1.0);
    fastNavFolder.add(fastNavParams, 'delayBeforeRestore');
    fastNavFolder.add(fastNavParams, 'delayBeforeRestoreSeconds', 0, 2.0);
    fastNavFolder.open();

    const edgesFolder = gui.addFolder('Edges');
    edgesFolder.add(edgeParams, 'edges');
    edgesFolder.open();

    const pbrFolder = gui.addFolder('PBR');
    pbrFolder.add(pbrParams, 'pbrEnabled');
    pbrFolder.open();

    const saoFolder = gui.addFolder('SAO');
    saoFolder.add(saoParams, 'enabled');
    saoFolder.add(saoParams, 'bias', -2, 2);
    saoFolder.add(saoParams, 'intensity', 0, 1.0);
    saoFolder.add(saoParams, 'scale', .05, 5);
    saoFolder.add(saoParams, 'kernelRadius', 1, 200);
    saoFolder.add(saoParams, 'minResolution', 0, 0.001);
    saoFolder.add(saoParams, 'numSamples', 4, 100);
    saoFolder.add(saoParams, 'blendFactor', 0, 2);
    saoFolder.add(saoParams, 'blendCutoff', 0, 2);
    saoFolder.add(saoParams, 'blur');
    saoFolder.open();

    const cameraFolder = gui.addFolder('Camera');
    cameraFolder.add(saoParams, 'perspective');
    cameraFolder.add(saoParams, 'far', 100, 20000);
    cameraFolder.add(saoParams, 'fov', 10, 120);
    cameraFolder.open();

    const customContainer = document.getElementById('myDatGuiContainer');
    customContainer.appendChild(gui.domElement);

</script>
</html>